home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / baycom / ax25.asm next >
Encoding:
Assembly Source File  |  1993-01-05  |  65.1 KB  |  2,318 lines

  1. ;AX.25 packet driver for RS232 port by Pawel Jalocha
  2. ;version of 4th January 1993
  3.  
  4. ;The purpose of this driver is to provide interface
  5. ;between simple modem (a la BAYCOM) and higher level application
  6. ;software like KA9Q NOS.
  7.  
  8. ;ax25.com emulates HDLC chip, buffers frames
  9. ;and serves software interface to higher applications
  10. ;by mean of a selected software interrupt.
  11.  
  12. ;Driver's calling convention conforms to "FTP packet driver specification"
  13. ;but no other tests than with NOS have been done.
  14.  
  15. ;Currently the driver provides only _one_ handle at a time.
  16.  
  17. ;To make ax25.com from ax25.asm execute two commands:
  18. ;    tasm ax25
  19. ;    tlink /t ax25
  20.  
  21. ;Free license for this software is herein granted for all radio _amateurs_
  22. ;Commercial usage in whole or part is prohibited.
  23.  
  24. ;Questions may be addressed to:
  25. ;       email:  jalocha@chopin.ifj.edu.pl
  26. ;       or      jalocha@vxcern.cern.ch
  27. ;       packet: SR9VRC@SP9ZDN.POL.EU (don't know whether it really works)
  28.  
  29. ;  To change ... => look for definition of ...
  30. ;
  31. ;o Maximum frame size the driver can receive => MaxFrameLen
  32. ;
  33. ;o Maximum amount of data the driver can transmit per one PTT push
  34. ;  and thus maximum transmition frame length => TxBufferLen
  35. ;
  36. ;o Size of an auxiliary buffer storing periods between signal
  37. ;  transitions before they are analyzed => RxBufferLen
  38. ;
  39. ;  If you don't need large frames you may save some kilobytes
  40. ;  of resident RAM by making MaxFrameLen and TxBufferLen smaller.
  41. ;
  42. ;  For low speeds you may as well carefully decrease RxBufferLen
  43.  
  44. ;You may want to change drvr_class to SLIP and run point-to-point direct IP
  45.  
  46. ;==============================================================
  47.  
  48. ax25_code       segment word public
  49.         assume  cs:ax25_code, ds:ax25_code
  50.  
  51.     org 2ch
  52. phd_environ     dw ?
  53.  
  54.     org 100h                ;when .com file is executed DOS jumps here
  55. start:  jmp install_driver
  56.  
  57.         even
  58. ;primary parameters with default values
  59. packet_int_no   db 60h  ;software interrupt
  60. com_irq         db 4    ;COM port IRQ
  61. com_irq_prio    db 0    ;if non-zero the IRQ should have highest priority.
  62. com_irq_share   db 0    ;if non-zero the IRQ is shared with other driver(s).
  63. com_base        dw 3f8h ;COM port base
  64. bit_rate        dw 1200 ;speed in bits per second
  65. tx_head         dw 240  ;transmition header length in bits
  66. tx_tail         dw 24   ;transmition tail length in bits
  67. slot_time       db 120  ;slot time for carrier sensing in bits
  68. persistance     db 64   ;p-persistence
  69. carrier_sense   db 2    ;carrier sensing
  70.             ;0 - don't care - just transmit (full duplex)
  71.             ;1 - sense DCD line
  72.             ;2 - sense data transitions (BAYCOM-like)
  73.             ;3 - deliver from data analysis (software DCD)
  74. drvr_class      db 9    ;driver's class - default is 9 that is AX.25
  75. sound           db 0    ;sound effects enabled if non-zero
  76. walk_step_div   db 16   ;bit_time/walk_step ratio
  77. dcd_thres       dw 50   ;DCD squelch threshold (0..100)
  78.  
  79. ;secondary parameters computed from primary ones
  80. cl_bit_len      dw 0    ;bit len in system clock (8253/4) ticks
  81. cl_bit_len_2    dw 0    ;half bit len in clock ticks
  82. cl_dcd_thres    dw 0    ;DCD threshold in clock ticks
  83. walk_step       dw 0    ;random walk step for DPLL
  84. bd_slot_time    dw 0    ;slot time in baud generator ticks / 8
  85. bd_bit_len      db 0    ;bit len in baud generator ticks / 8
  86. irq_mask        db 0    ;IRQ mask for 8259
  87.  
  88. driver_name     db 'AX.25 driver for BAYCOM-style modem',0
  89.  
  90.         even
  91. old_packet_int  dw 0,0  ;saves software interrupt vector
  92. receive_upcall  dw 0,0  ;keeps application "upcall" routine address
  93.  
  94. ;ANSI sequences to set char. rendition.
  95. ;if you don't like these or have problems with ANSI
  96. ;make null strings definitions like: BLINK equ ''
  97.  
  98. NORM    equ     27,'[0m'
  99. BLINK   equ     27,'[5m'
  100. REVERS  equ     27,'[7m'
  101. BOLD    equ     27,'[1m'
  102.  
  103. ;==============================================================
  104. ;Service routine for software interrupt to control the driver
  105.  
  106. ;  Packet Driver Error numbers
  107. NO_ERROR        equ     0       ;no error at all.
  108. BAD_HANDLE      equ     1       ;invalid handle number
  109. NO_CLASS        equ     2       ;no interfaces of specified class found
  110. NO_TYPE         equ     3       ;no interfaces of specified type found
  111. NO_NUMBER       equ     4       ;no interfaces of specified number found
  112. BAD_TYPE        equ     5       ;bad packet type specified
  113. NO_MULTICAST    equ     6       ;this interface does not support multicast
  114. CANT_TERMINATE  equ     7       ;this packet driver cannot terminate
  115. BAD_MODE        equ     8       ;an invalid receiver mode was specified
  116. NO_SPACE        equ     9       ;operation failed because of insufficient space
  117. TYPE_INUSE      equ     10      ;the type had previously been accessed, and not released.
  118. BAD_COMMAND     equ     11      ;the command was out of range, or not implemented
  119. CANT_SEND       equ     12      ;the packet couldn't be sent (usually hardware error)
  120. CANT_SET        equ     13      ;hardware address couldn't be changed (more than 1 handle open)
  121. BAD_ADDRESS     equ     14      ;hardware address has bad length or format
  122. CANT_RESET      equ     15      ;Couldn't reset interface (more than 1 handle open).
  123. BAD_IOCB        equ     16      ;an invalid iocb was specified
  124.  
  125. regs_w  struc                           ; stack offsets of incoming regs
  126. _ES     dw      ?
  127. _DS     dw      ?
  128. _BP     dw      ?
  129. _DI     dw      ?
  130. _SI     dw      ?
  131. _DX     dw      ?
  132. _CX     dw      ?
  133. _BX     dw      ?
  134. _AX     dw      ?
  135. _IP     dw      ?
  136. _CS     dw      ?
  137. _F      dw      ?                       ; flags, Carry flag is bit 0
  138. regs_w  ends
  139.  
  140. CY      equ     0001h
  141. EI      equ     0200h
  142.  
  143. regs_b  struc                           ; stack offsets of incoming regs
  144.     dw      ?                       ; es, ds, bp, di, si are 16 bits
  145.     dw      ?
  146.     dw      ?
  147.     dw      ?
  148.     dw      ?
  149. _DL     db      ?
  150. _DH     db      ?
  151. _CL     db      ?
  152. _CH     db      ?
  153. _BL     db      ?
  154. _BH     db      ?
  155. _AL     db      ?
  156. _AH     db      ?
  157. regs_b  ends
  158.  
  159. DRVR_ISR:                       ;service interrupt vector points here
  160.                 ;application layer calls enter this point
  161.                 ;via a software interrupt
  162.     jmp exec_command        ;entry point must be a jump
  163.     db 'PKT DRVR',0         ;followed by this string
  164.  
  165. exec_command:           ;packet driver command executor
  166.     sti             ;don't lock interrupts
  167.     push ax         ;save registers on stack
  168.     push bx
  169.     push cx
  170.     push dx
  171.     push si
  172.     push di
  173.     push bp
  174.     push ds
  175.     push es
  176.     mov  bp,sp              ;bp=sp so we can address pushed registers
  177.     and _F[bp],not CY       ;Clear carry on exit
  178.     mov bx,cs               ;make ds=cs
  179.     mov ds,bx
  180.     mov bl,ah               ;execute command given by ah
  181.     mov bh,0
  182.     cmp bx,26
  183.     jnc f_above_25
  184.     add bx,bx
  185.     call [functions+bx]
  186. DRVR_ISR_return:
  187.     mov _DH[bp],dh          ;pass dh-now to dh-on-exit
  188.     sbb ax,ax
  189.     and ax,CY
  190.     or _F[bp],ax            ;pass carry-now to carry-on-exit
  191.     pop es
  192.     pop ds
  193.     pop bp
  194.     pop di
  195.     pop si
  196.     pop dx
  197.     pop cx
  198.     pop bx
  199.     pop ax
  200.     iret
  201.  
  202. f_above_25:
  203.     call f_not_implemented
  204.     jmp short DRVR_ISR_return
  205.  
  206.     even
  207. functions       label   word
  208.     dw      f_not_implemented       ;0
  209.     dw      f_driver_info           ;1
  210.     dw      f_access_type           ;2
  211.     dw      f_release_type          ;3
  212.     dw      f_send_pkt              ;4
  213.     dw      f_terminate             ;5
  214.     dw      f_get_address           ;6
  215.     dw      f_reset_interface       ;7
  216.     dw      f_stop                  ;8
  217.     dw      f_not_implemented       ;9
  218.     dw      f_get_parameters        ;10
  219.     dw      f_not_implemented       ;11
  220.     dw      f_as_send_pkt           ;12
  221.     dw      f_drop_pkt              ;13
  222.     dw      f_not_implemented       ;14
  223.     dw      f_not_implemented       ;15
  224.     dw      f_not_implemented       ;16
  225.     dw      f_not_implemented       ;17
  226.     dw      f_not_implemented       ;18
  227.     dw      f_not_implemented       ;19
  228.     dw      f_set_rcv_mode          ;20
  229.     dw      f_get_rcv_mode          ;21
  230.     dw      f_set_multicast_list    ;22
  231.     dw      f_get_multicast_list    ;23
  232.     dw      f_get_statistics        ;24
  233.     dw      f_set_address           ;25
  234.  
  235. f_driver_info:
  236.     mov dh,drvr_class               ;driver class
  237.     mov _CH[bp],dh
  238.     mov _AL[bp],1                   ;basic flag
  239.     mov _DX[bp],0                   ;driver type
  240.     mov _CL[bp],0                   ;driver number
  241.     mov _BX[bp],0                   ;driver version
  242.     mov _DS[bp],ds                  ;driver name pointer
  243.     mov _SI[bp],offset driver_name
  244.     mov dh,NO_ERROR
  245.     clc
  246.     ret
  247.  
  248. f_access_type:
  249.     mov bx,_BX[bp]
  250.     cmp al,drvr_class       ;our class ?
  251.     jnz wrong_class
  252.     cmp bx,0FFFFh           ;generic type ?
  253.     jz type_OK
  254.     cmp bx,0                ;our type ?
  255.     jnz wrong_type
  256. type_OK:
  257.     cmp dl,0                ;generic num ?
  258.     jnz wrong_num
  259.     mov ax,receive_upcall   ;check if handle busy
  260.     or ax,receive_upcall+2
  261.     jnz busy_handle
  262.     mov receive_upcall,di   ;store receiver upcall
  263.     mov ax,es
  264.     mov receive_upcall+2,ax
  265.     mov _AX[bp],0           ;return handle=0
  266.     clc
  267.     mov dh,NO_ERROR
  268.     ret
  269.  
  270. wrong_class:
  271.     stc
  272.     mov dh,NO_CLASS
  273.     ret
  274. wrong_type:
  275.     stc
  276.     mov dh,NO_TYPE
  277.     ret
  278. wrong_num:
  279.     stc
  280.     mov dh,NO_NUMBER
  281.     ret
  282. busy_handle:
  283.     stc
  284.     mov dh,TYPE_INUSE
  285.     ret
  286.  
  287. f_stop: jmp short clear_upcall
  288.  
  289. f_release_type:
  290.     cmp _BX[bp],0           ;handle=0 ?
  291.     jnz wrong_handle
  292.     mov ax,receive_upcall   ;is receiver upcall defined ?
  293.     or ax,receive_upcall+2
  294.     jz wrong_handle         ;jump if not
  295. clear_upcall:
  296.     xor ax,ax               ;receiver upcall := 0:0
  297.     mov receive_upcall,ax   ;this means "upcall address is not valid"
  298.     mov receive_upcall+2,ax
  299.     clc
  300.     mov dh,NO_ERROR
  301.     ret
  302.  
  303. wrong_handle:
  304.     stc
  305.     mov dh,BAD_HANDLE
  306.     ret
  307.  
  308. f_send_pkt:             ;_DS:si=data, cx=length
  309.     mov es,_DS[bp]  ;es:si=packet address, cx=packet length
  310.  
  311.     mov bx,cx       ;save packet length
  312.     mov ah,7Eh      ;starting HDLC flag
  313.     call AddTxByteDirect    ;add to TxBuffer without bit stuffing
  314.     jc PacketTooBig         ;jump if TxBuffer full
  315.     mov dx,0FFFFh           ;initialize CRC
  316. AddNextByte:
  317.       mov ah,es:[si]        ;take next packet byte
  318.       inc si                ;increment pointer
  319.       call CRCpass          ;pass through CRC
  320.       call AddTxByteStuffing ;add the byte to TxBuffer _with_ stuffing
  321.       jc PacketTooBig       ;jump if TxBuffer overloaded
  322.     loop AddNextByte        ;loop over packet's bytes
  323.     not dx                  ;complete CRC computation by inverting all bits
  324.     mov ax,dx               ;append CRC
  325.     xchg al,ah              ;lower byte first
  326.     call AddTxByteStuffing  ;add to TxBuffer with stuffing
  327.     jc PacketTooBig
  328.     xchg al,ah              ;higher byte now
  329.     call AddTxByteStuffing  ;add to TxBuffer
  330.     jc PacketTooBig
  331.     mov ah,7Eh              ;ending HDLC flag
  332.     call AddTxByteDirect    ;add to TxBuffer _without_ bit stuffing
  333.     jc PacketTooBig
  334.     call TxFlush8bit        ;ensure that TxBuffer data ends at byte boundary
  335.     jz PacketTooBig
  336.  
  337.     call ValidateTxBlock    ;make the packet we just put into buffer
  338.                 ;valid for transmition
  339.  
  340.     mov ax,bx               ;increment bytes_out counter
  341.     mov bx,offset bytes_out ;note that we still had the packet length in bx
  342.     call inc_dword_bx_by_ax ;dword [bx]+=ax
  343.  
  344.     mov bx,offset packets_out       ;increment packet_out counter
  345.     call inc_dword_bx       ;dword [bx]+=1
  346.  
  347. ;Note that packets_out/bytes_out counts data sent by application
  348. ;that is _not_ the data transmitted from the buffer.
  349. ;Data sent by application will be transmitted on the air
  350. ;only after the driver goes into transmit state that is
  351. ;after it "pushed" PTT.
  352.  
  353.     clc
  354.     mov dh,NO_ERROR
  355.     ret
  356.  
  357. PacketTooBig:
  358.     call TxFlush8bit                ;???
  359.     call CancelTxBlock              ;cancel the block we were writing
  360.                     ;into Tx buffer
  361.     mov bx,offset errors_out        ;increment errors_out counter
  362.     call inc_dword_bx
  363.  
  364.     stc
  365.     mov dh,CANT_SEND
  366.     ret
  367.  
  368. f_terminate:
  369.     call deinstall_driver
  370.     clc
  371.     mov dh,NO_ERROR
  372.     ret
  373.  
  374. f_get_address:          jmp short f_not_implemented
  375. f_reset_interface:      jmp short f_not_implemented
  376. f_get_parameters:       jmp short f_not_implemented
  377. f_as_send_pkt:          jmp short f_not_implemented
  378. f_drop_pkt:             jmp short f_not_implemented
  379. f_set_rcv_mode:         jmp short f_not_implemented
  380. f_get_rcv_mode:         jmp short f_not_implemented
  381. f_set_multicast_list:   jmp short f_not_implemented
  382. f_get_multicast_list:   jmp short f_not_implemented
  383.  
  384. f_get_statistics:
  385.     mov _DS[bp],ds
  386.     mov _SI[bp],offset statistics_list
  387.     clc
  388.     mov dh,NO_ERROR
  389.     ret
  390.  
  391. f_set_address:          jmp short f_not_implemented
  392.  
  393. f_not_implemented:              ;non-implemented functions jump here
  394.     stc                     ;set carry to indicate an error
  395.     mov dh,BAD_COMMAND      ;error code = BAD_COMMAND
  396.     ret
  397.  
  398.         even
  399. statistics_list label   dword   ;statistics variables
  400. packets_in      dw      0,0     ;as in packet driver specification
  401. packets_out     dw      0,0
  402. bytes_in        dw      0,0
  403. bytes_out       dw      0,0
  404. errors_in       dw      0,0
  405. errors_out      dw      0,0
  406. packets_dropped dw      0,0
  407.  
  408.                 ;extended statistics (not in use yet)
  409. ;PTT_pushes     dw      0,0
  410. ;HDLC_flags     dw      0,0
  411. ;frame_aborts   dw      0,0
  412. ;short_packets  dw      0,0     ;counts valid but too short packets
  413.  
  414. ;Simple routines to increment double words
  415.  
  416. inc_dword_bx_by_ax:             ;increments dword ds:[bx] by ax
  417.     add word ptr [bx],ax
  418.     inc bx
  419.     inc bx
  420.     adc word ptr [bx],0
  421.     dec bx
  422.     dec bx
  423.     ret
  424.  
  425. inc_dword_bx:           ;increments dword ds:[bx] by 1
  426.     add word ptr [bx],1
  427.     inc bx
  428.     inc bx
  429.     adc word ptr [bx],0
  430.     dec bx
  431.     dec bx
  432.     ret
  433.  
  434. ;==============================================================
  435.  
  436.         even
  437. OldTimerISR     dw 0,0  ;saves Timer interrupt service routine address
  438. TimerISRActive  db 0
  439.  
  440.             ;This routine re-routes Timer interrupt so
  441.             ;Timer_ISR is called at every timer tick
  442. Initialize_Timer:
  443.     pushf           ;save flags
  444.     cli             ;disable interrupts while we play with vectors
  445.             ;is it really needed ?
  446.  
  447.     push ax         ;save ax
  448.                 ;now save current timer int. vector
  449.     push es                 ;save es and bx
  450.     push bx
  451.     mov al,8                ;save Timer interrupt vector
  452.     mov ah,35h              ;35h is "get vector" function
  453.     int 21h                 ;call DOS service
  454.     mov OldTimerISR,bx      ;now vector in es:bx - we have to save it
  455.     mov ax,es
  456.     mov OldTimerISR+2,ax
  457.     pop bx                  ;restore es and bx
  458.     pop es
  459.                 ;set new vector
  460.     push dx
  461.     mov al,8                ;Timer vector is number 8
  462.     mov ah,25h              ;25h is "set vector" function
  463.     mov dx,offset Timer_ISR ;ds:dx := Timer_ISR address
  464.     int 21h
  465.     pop dx
  466.  
  467.     pop ax          ;restore ax
  468.  
  469.     popf            ;restore flags (including interrupt flag)
  470.     ret
  471.  
  472.  
  473. Restore_Timer:          ;This routine restores Timer interrupt vector
  474.             ;so Timer_ISR is not called any more
  475.  
  476.     pushf           ;save flags
  477.     push ax
  478.     cli             ;and disable interrupts
  479.  
  480.                     ;restore interrupt vector
  481.     push ds                         ;save ds
  482.     push dx
  483.     mov al,8                        ;timer int. number
  484.     mov ah,25h                      ;"set vector" function
  485.     lds dx,dword ptr OldTimerISR    ;ds:dx := vector address
  486.     int 21h                         ;call DOS
  487.     pop dx
  488.     pop ds                          ;restore ds
  489.  
  490.     pop ax
  491.     popf            ;restore flags
  492.     ret
  493.  
  494. Timer_ISR:
  495.     sti                     ;shall we enable interrupts already here ?
  496.     pushf                   ;first call the old timer service routine
  497.     call dword ptr cs:[OldTimerISR]
  498.     push ax                 ;save all used registers
  499.     push bx                 ;because we are in an interrupt
  500.     push cx
  501.     push dx
  502.     push ds
  503.     mov ax,cs               ;make ds=cs
  504.     mov ds,ax
  505.     mov al,TimerISRActive   ;check for possible recursive call
  506.     and al,al
  507.     jnz TimerISR_already_Active     ;jump if it is so
  508.     mov al,0FFh             ;mark Timer ISR as active now
  509.     mov TimerISRActive,al
  510.     sti                     ;enable interrupts so Tx and Rx can run
  511.                 ;while we process data collected by Rx
  512.     mov al,RxSoundActive    ;if we activated receive beep before
  513.     and al,al
  514.     jz ReadNextRxPeriod
  515.       in al,61h             ;we terminate it now.
  516.       and al,0FCh
  517.       out 61h,al
  518.       xor al,al
  519.       mov RxSoundActive,al
  520.  
  521. ReadNextRxPeriod:
  522.       call ReadRxPeriod     ;read next Rx period into ax
  523.       jnc TimerISR_end      ;jump if buffer empty
  524.       call ProcessPeriod    ;here we process the period
  525.       jmp short ReadNextRxPeriod  ;loop
  526. TimerISR_end:
  527.     xor al,al
  528.     mov TimerISRActive,al
  529. TimerISR_already_active:
  530.     pop ds                  ;restore registers
  531.     pop dx
  532.     pop cx
  533.     pop bx
  534.     pop ax
  535.     iret
  536.  
  537. MaxFrameLen     equ 2050                ;maximum frame length in bytes
  538.                     ;including CRC
  539. MinFrameLen     equ 17                  ;minimal length for AX.25 frame
  540.                     ;any shorter frame is discarded
  541.  
  542. ;AX.25 frames have minimal length of 17 bytes but you may want to
  543. ;make this limit lower if you are going to run other protocol...
  544.  
  545.         even
  546. RxFrameData     db  MaxFrameLen dup(0)  ;frame buffer
  547. RxFrameLen      dw  0                   ;actual frame length (counts bytes)
  548. RxBitCount      db  0                   ;count single data bits
  549. RxFrameValid    db  0                   ;non-zero if frame is valid
  550. SamplePhase     dw  0                   ;time to next sampling point
  551. SampleLevel     dw  0                   ;low=current data bit, high=prev. bit
  552. RxByteReg       db  0                   ;receiver shift register
  553. RxStuffing      db  0
  554. RxShiftReg      db  0
  555. RxSoundActive   db  0
  556.  
  557. ProcessPeriod:                  ;on input ax=period
  558.     mov cx,SampleLevel      ;bit 0,cl=level, bit 0,ch=previous level
  559.     xor cl,1                ;flip level
  560.     mov dx,SamplePhase      ;dx=time to next sample
  561. MoveSampling:
  562.       cmp ax,dx             ;compare SamplePhase with period
  563.       jc PeriodLower        ;jump if Period lower
  564.       sub ax,dx             ;subtract SamplePhase from period
  565.       xor ch,cl             ;xor level with previous level
  566.       call NewRxBit         ;analyze bit in bit 0,ch
  567.       mov ch,cl             ;previous level = level
  568.       mov dx,cl_bit_len     ;load SamplePhase with bit length
  569.       jmp short MoveSampling      ;loop
  570. PeriodLower:
  571.     sub dx,ax               ;subtract period from SamplePhase
  572.     mov SampleLevel,cx      ;save SampleLevel
  573.  
  574.                 ;now comes rather primitive DPLL
  575.     mov ax,cl_bit_len_2     ;load half bit period
  576.     sub ax,dx               ;subtract SamplePhase
  577.                 ;now: dx=SamplePhase, ax=phase error
  578.     cmp ax,walk_step
  579.     jle check_neg
  580.       add dx,walk_step
  581.       jmp short save_SamplePhase
  582. check_neg:        
  583.     neg ax
  584.     cmp ax,walk_step
  585.     jle save_SamplePhase
  586.       sub dx,walk_step
  587.  
  588. ;        add ax,8                ;offset error to compensate
  589. ;                                ;for "sar" arithmetic error
  590. ;        mov cl,4                ;divide the error by 16
  591. ;        sar ax,cl
  592. ;        add dx,ax               ;add correction to SamplePhase
  593.  
  594. save_SamplePhase:        
  595.     mov SamplePhase,dx      ;save SamplePhase
  596.     ret
  597.  
  598. NewRxBit:               ;bit 0,ch = _inverted_ data bit to append to the frame
  599.             ;ax,cx,dx must not be modified
  600.     push ax
  601.     push cx
  602.     push dx
  603.     mov al,RxShiftReg       ;load shift reg.
  604.     shl al,1                ;append data bit (which is still inverted)
  605.     or al,ch
  606.     mov RxShiftReg,al       ;save shift reg.
  607.     cmp al,81h              ;check for HDLC flag (01111110 pattern)
  608.     jz RxFoundFlag          ;jump if so
  609.     test al,7Fh             ;check for invalid frame (7 1s in a row)
  610.     jz RxFrameInvalid       ;jump if so
  611.  
  612.     mov al,RxFrameValid     ;is frame still valid ?
  613.     and al,al
  614.     jz NewRxBit_ret         ;jump if so
  615.     xor ch,1                ;invert bit to append (it was inverted at entry)
  616.     mov al,RxByteReg        ;load byte reg.
  617.     mov ah,RxBitCount
  618.     and ch,1                ;zero to append ?
  619.     jz AppendZeroBit
  620.     ror ch,1                ;carry=data bit (must be 1 here...)
  621.     rcr al,1                ;append data bit to byte buffer
  622.     inc ah                  ;increase bit counter
  623.     inc RxStuffing          ;increase stuffing flag
  624.     jmp short CheckBitCount
  625. AppendZeroBit:
  626.     xor cl,cl
  627.     mov ch,RxStuffing       ;check for stuffing
  628.     cmp ch,5
  629.     mov RxStuffing,cl       ;clear stuffing flag
  630.     jz NewRxBit_ret         ;avoid adding zero bit
  631.       shr al,1              ;append zero bit
  632.       inc ah
  633. CheckBitCount:
  634.     mov RxByteReg,al        ;save byte reg.
  635.     mov RxBitCount,ah       ;save bit counter
  636.     test ah,07h             ;check for byte boundary
  637.     jnz NewRxBit_ret
  638.       mov bx,RxFrameLen     ;load frame length
  639.       cmp bx,MaxFrameLen    ;check frame size
  640.       jnc RxFrameInvalid    ;jump if frame would exceed max. length
  641.       mov [RxFrameData+bx],al
  642.       inc bx
  643.       mov RxFrameLen,bx     ;save new frame length
  644. NewRxBit_ret:
  645.     pop dx
  646.     pop cx
  647.     pop ax
  648.     ret
  649. RxFrameInvalid:
  650.     xor al,al
  651.     mov RxFrameValid,al
  652.     jmp short NewRxBit_ret
  653. RxFoundFlag:
  654.     mov al,RxFrameValid     ;frame valid ?
  655.     and al,al
  656.     jz PrepareNewFrame      ;jump if not valid
  657.     mov al,RxBitCount       ;check bit count
  658.     inc al
  659.     test al,07h             ;check if multiply of 8
  660.     jnz PrepareNewFrame     ;jump if not
  661.     mov cx,RxFrameLen       ;check frame length
  662.     cmp cx,MinFrameLen
  663.     jc PrepareNewFrame      ;jump if length less then minimum
  664.     
  665.     mov dx,0FFFFh           ;initialize CRC
  666.     mov bx,offset RxFrameData      ;pass frame bytes through CRC except last two
  667.     sub cx,2                ;decrease length by 2
  668. PassNextRxbyte:
  669.       mov ah,[bx]           ;load next byte
  670.       inc bx
  671.       call CRCpass          ;pass it through CRC
  672.     loop PassNextRxByte
  673.     not dx                  ;negate CRC
  674.     cmp dl,[bx]             ;check lower CRC byte
  675.     jnz BadCRCFrame         ;jump if bad
  676.     inc bx
  677.     cmp dh,[bx]             ;check high CRC byte
  678.     jnz BadCRCFrame         ;jump if bad
  679.  
  680.     mov al,sound            ;if sound effects activated
  681.     and al,al
  682.     jz UpCallAppl
  683.       mov RxSoundActive,al  ;make receive beep
  684.       xor al,al
  685.       out 42h,al
  686.       mov al,2
  687.       out 42h,al
  688.       in al,61h
  689.       or al,3
  690.       out 61h,al
  691.  
  692. UpCallAppl:
  693. ;Frame is OK !!! - do the upcall to the application layer.
  694.     call DoUpCall
  695.  
  696. PrepareNewFrame:
  697.     xor ax,ax               ;null frame lemgth
  698.     mov RxFrameLen,ax
  699.     mov RxBitCount,al       ;null bit count
  700.     mov RxStuffing,al       ;initialize bit stuffing
  701.     mov al,0FFh
  702.     mov RxFrameValid,al     ;mark frame as valid
  703.     jmp short NewRxBit_ret
  704.  
  705. BadCRCFrame:                    ;shall we really count bad CRC packets
  706.                 ;as "errors_in" ?
  707.  
  708.     mov bx,offset errors_in ;increment "errors_in"
  709.     call inc_dword_bx
  710.  
  711.     jmp short PrepareNewFrame     ;abort current frame and make ready for new one
  712.  
  713. DoUpCall:               ;input: RxFrameData contains a valid packet (CRC is OK)
  714.             ;       RxFrameLen contains its length
  715.     push ds
  716.     push es
  717.     push si
  718.     push di
  719.  
  720.     mov bx,offset packets_in        ;increment input packet counter
  721.     call inc_dword_bx
  722.  
  723.     mov cx,RxFrameLen       ;load packet length
  724.     sub cx,2                ;exclude CRC
  725.  
  726.     mov ax,cx               ;increment input bytes counter by packet length
  727.     mov bx,offset bytes_in
  728.     call inc_dword_bx_by_ax
  729.  
  730.     mov ax,receive_upcall   ;is there a valid upcall address ?
  731.     or ax,receive_upcall+2
  732.     jz drop_packet          ;jump if there is not
  733.  
  734.     mov ax,0                ;flag=0 - first upcall
  735.     mov bx,0                ;handle = 0
  736.     mov di,0                ;set es:di = NULL
  737.     mov es,di
  738.     call dword ptr [receive_upcall] ;first upcall
  739.     mov ax,es               ;check if application returned
  740.     or ax,di                ;valid buffer pointer
  741.     jz drop_packet          ;jump if not
  742. ;       jz DoUpCall_ret
  743.  
  744.     mov si,di               ;make si=di before we alter di (for second upcall)
  745.     mov bx,offset RxFrameData ;copy the packet to es:di
  746.     mov cx,RxFrameLen       ;packet length (exclude CRC)
  747.     sub cx,2
  748. CopyLoop: mov al,[bx]           ;loop over packet bytes
  749.       inc bx                ;would movsb do the job ?
  750.       mov es:[di],al
  751.       inc di
  752.       loop CopyLoop
  753.     mov cx,RxFrameLen       ;again packet len for second call
  754.     sub cx,2                ;and without CRC
  755.     mov ax,es               ;make ds=es (ds not same as cs now !)
  756.     mov ds,ax
  757.     mov bx,0                ;handle 0
  758.     mov ax,1                ;flag=1 - second upcall
  759.     call dword ptr cs:[receive_upcall]      ;second upcall
  760. ;we have to use cs: addressing in above call because we modified ds
  761.  
  762. DoUpCall_ret:
  763.     pop di
  764.     pop si
  765.     pop es
  766.     pop ds
  767.     ret
  768.  
  769. drop_packet:
  770.     mov bx,offset packets_dropped   ;increment dropped packet counter
  771.     call inc_dword_bx
  772.     jmp short DoUpCall_ret
  773.  
  774. ;Note that packets_dropped counts packets refused by the application
  775. ;on the first upcall. I assume application refuses to take the packet
  776. ;by returning NULL pointer
  777.  
  778. ;==============================================================
  779.         even
  780. save_IER        db 0
  781. save_LCR        db 0
  782. save_MCR        db 0
  783. save_DLL        db 0
  784. save_DLM        db 0
  785. save_irq_en     db 0FFh ;save irq enabled in 8259
  786. save_ISR        dw 0,0  ;saved interrupt vector
  787.  
  788. initialize_COM:
  789.     push ax
  790.     push dx
  791.     pushf                   ;save CPU interrupt flag
  792.     cli                     ;disable interrupts
  793.  
  794.     push es
  795.     push bx
  796.     mov al,com_irq          ;save COM interrupt vector
  797.     add al,8
  798.     mov ah,35h
  799.     int 21h
  800.     mov save_ISR,bx
  801.     mov ax,es
  802.     mov save_ISR+2,ax
  803.     pop bx
  804.     pop es
  805.  
  806.     mov al,com_irq          ;set new vector
  807.     add al,8
  808.     mov ah,25h
  809.     mov dx,offset COM_ISR
  810.     int 21h
  811.  
  812.     mov ah,irq_mask
  813.     not ah
  814.     in al,21h               ;read 8259 mask
  815.     or al,ah                ;extract com irq mask
  816.     mov save_irq_en,al      ;save it
  817.     in al,21h               ;enable com irq in 8259
  818.     and al,ah               ;by clearing the right bit.
  819.     out 21h,al
  820.  
  821.     mov al,com_irq_prio     ;IRQ priority requested ?
  822.     and al,al
  823.     jz save_com_reg         ;jump if not
  824.       mov al,com_irq
  825.       sub al,1
  826.       and al,07h
  827.       or al,0C0h
  828.       out 20h,al            ;out 20h,(C0h + (irq-1)&7)
  829.  
  830. save_com_reg:                   ;save COM registers
  831.     mov dx,com_base         ;dx=com_base
  832.     inc dx                  ;dx=IER
  833.     in al,dx                ;save IER
  834.     mov save_IER,al
  835.     xor al,al               ;disable all COM interrupts
  836.     out dx,al
  837.     add dx,2                ;dx=LCR
  838.     in al,dx                ;save LCR
  839.     mov save_LCR,al
  840.     inc dx                  ;dx=MCR
  841.     in al,dx                ;save MCR
  842.     mov save_MCR,al
  843.     dec dx                  ;dx=LCR
  844.     mov al,81h
  845.     out dx,al               ;enable divisor read/write
  846.     sub dx,3                ;dx=com_base=DLL
  847.     in al,dx                ;read DLL
  848.     mov save_DLL,al         ;save it
  849.     inc dx                  ;dx=DLM
  850.     in al,dx                ;read DLM
  851.     mov save_DLM,al         ;save it
  852.     dec dx                  ;dx=com_base=DLL
  853.     mov ax,bd_slot_time     ;set rate divisor
  854.                 ;to slot_time*bd_bit_len
  855.     out dx,al
  856.     inc dx
  857.     xchg al,ah
  858.     out dx,al
  859.     add dx,2                ;dx=LCR
  860.     mov al,01h              ;set 6 data/1 stop/no parity format
  861.     out dx,al
  862.     inc dx                  ;dx=MCR
  863.     mov al,09h              ;set DTR high, RTS low.
  864.     out dx,al               ;and OUT2 high
  865.     sub dx,3                ;dx=IER
  866.     mov al,0Ah
  867.     out dx,al               ;enable TxEmpty and modem status interrupt
  868.     add dx,4                ;dx=LSR
  869.     in al,dx                ;read LSR
  870.     inc dx                  ;to clear possible line status int.
  871.     in al,dx                ;read MSR to clear possible modem status int
  872.     sub dx,6                ;dx=com_base again
  873.     in al,dx                ;read Rx buffer
  874.     in al,dx                ;to clear any possible pending Rx int.
  875.     mov al,000000b          ;load Tx with 000000 char
  876.     out dx,al
  877.     out dx,al
  878.  
  879.     popf                    ;restore interrupt flag
  880.     pop dx
  881.     pop ax
  882.     ret
  883.  
  884. restore_COM:
  885.     pushf
  886.     push ax
  887.     push dx
  888.     cli
  889.  
  890.     push ds
  891.     mov al,com_irq          ;restore interrupt vector
  892.     add al,8
  893.     mov ah,25h
  894.     lds dx,dword ptr save_ISR
  895.     int 21h
  896.     pop ds
  897.  
  898.     in al,21h               ;restore int. enable in 8259
  899.     or al,irq_mask
  900.     and al,save_irq_en
  901.     out 21h,al
  902.  
  903.     mov al,com_irq_prio     ;was IRQ priority requested ?
  904.     and al,al
  905.     jz restore_com_reg      ;jump if not
  906.       mov al,0C7h
  907.       out 20h,al            ;out 20h,C7h
  908.  
  909. restore_com_reg:
  910.     mov dx,com_base
  911.     in al,dx                ;read Rx to clear a possible int.
  912.     in al,dx
  913.     inc dx                  ;dx=IER
  914.     mov al,save_IER         ;restore IER
  915.     out dx,al
  916.     add dx,2                ;dx=LCR
  917.     mov al,save_LCR         ;restore LCR
  918.     out dx,al
  919.     inc dx                  ;dx=MCR
  920.     mov al,save_MCR         ;restore MCR
  921.     out dx,al
  922.     inc dx                  ;dx=LSR
  923.     in al,dx                ;read LSR to clear possible int.
  924.     inc dx                  ;dx=MSR
  925.     in al,dx                ;read MSR to clear possible int.
  926.     sub dx,3                ;dx=LCR
  927.     in al,dx                ;enable rate divisor access
  928.     or al,80h
  929.     out dx,al
  930.     sub dx,3                ;dx=com_base=DLL
  931.     mov al,save_DLL         ;restore rate divisor
  932.     out dx,al
  933.     inc dx                  ;dx=DLM
  934.     mov al,save_DLM
  935.     out dx,al
  936.     add dx,2                ;dx=LCR
  937.     mov al,save_LCR         ;restore LCR again
  938.     out dx,al
  939.  
  940.     pop dx
  941.     pop ax
  942.     popf
  943.     ret
  944.  
  945.         even
  946. TxCountDown     dw 0    ;Down-counter to measure Tx bits within a byte.
  947. TxState         db 0    ;0 = idle
  948.             ;1 = sending head
  949.             ;2 = sending usefull data
  950.             ;3 = sending tail
  951.  
  952. COM_ISR:
  953.     push ax         ;save most often used registers on stack
  954.     push bx
  955.     push cx
  956.     push dx
  957.     push ds
  958.  
  959.     mov ax,cs
  960.     mov ds,ax
  961.     mov dx,com_base
  962.     add dx,2                ;dx=IIR
  963.  
  964.     in al,dx                ;load IIR
  965.     test al,000000001b      ;COM interrupt pending ?
  966.     jnz No_COM_Service      ;jump if not
  967.  
  968.     dec dx                  ;dx=IER
  969.     xor al,al
  970.     out dx,al               ;disable all COM interrupts
  971.     inc dx
  972.                 ;now check for possible interrupts sources
  973.     call serv_modem_state   ;dx=IIR and may not be changed
  974.     call serv_tx_empty
  975.  
  976.     mov al,20h              ;tell the interrupt controler
  977.     out 20h,al              ;that interrupt service is done.
  978.  
  979.     dec dx                  ;dx=IER
  980.     mov al,0Ah
  981.     out dx,al               ;enable TxEmpty and Modem Status interrupts
  982.     inc dx
  983.  
  984. End_ISR:
  985.     pop ds                  ;restore saved registers
  986.     pop dx
  987.     pop cx
  988.     pop bx
  989.     pop ax
  990.     iret
  991.  
  992. No_COM_service:
  993.     mov al,20h
  994.     out 20h,al
  995.     jmp short End_ISR
  996.  
  997.     even
  998. prev_timer_count dw 0
  999.  
  1000. serv_modem_state:               ;dx=IIR
  1001.     add dx,4                ;dx=MSR
  1002.     in al,dx                ;read MSR
  1003.     sub dx,4                ;dx=IIR
  1004.     test al,00000001b       ;did CTS change state ?
  1005.                 ;one could check any other input here...
  1006.     jz serv_modem_state_ret ;jump if not
  1007.     xor al,al               ;read system timer count
  1008.     out 43h,al
  1009.     in al,40h
  1010.     xchg al,ah
  1011.     in al,40h
  1012.     xchg al,ah              ;Timer value in ax now
  1013.     mov bx,ax               ;subtract previous count (Timer counts _down_ !)
  1014.     xchg ax,prev_timer_count
  1015.     sub ax,bx               ;so now ax contains the period elapsed
  1016.     shr ax,1                ;shift right as lowest bit is always zero
  1017.     call StoreRxPeriod      ;may not change dx,ax
  1018.     call UpdateDataStat
  1019. serv_modem_state_ret:
  1020.     ret                     ;dx=IIR
  1021.  
  1022. serv_tx_empty:          ;dx=IIR
  1023.     add dx,3        ;dx=LSR
  1024.     in al,dx
  1025.     sub dx,3        ;dx=IIR
  1026.     test al,00100000b ;THRE ?
  1027.     jz serv_tx_empty_ret
  1028.     mov bx,2        ;for faster add/sub dx,2
  1029.     sub dx,bx       ;dx=TxData
  1030.     xor al,al       ;load Tx with 0000000 char
  1031.     out dx,al
  1032.     add dx,bx       ;dx=IIR
  1033.     call [word ptr ServTx]  ;bx=2, dx must _not_ be modified
  1034. serv_tx_empty_ret:
  1035.     ret
  1036.  
  1037.     even
  1038. ServTx  dw ServTxIdle
  1039.  
  1040. ServTxIdle:                     ;here a decision about pushing PTT should be taken
  1041.     call Randomize          ;but first update RandomByte
  1042.     call TxBufferEmpty      ;any data to transmit ?
  1043.     jz ClearStat            ;jump if not
  1044.     call TxPTTDecision      ;ask PTT decision circuit for permision
  1045.     jnc ClearStat           ;jump if no permition
  1046.                 ;decision positive - enter transmit mode
  1047.     add dx,2                ;dx=MCR
  1048.     in al,dx
  1049.     or al,2                 ;set RTS high
  1050.     out dx,al               ;thus activate PTT
  1051.     dec dx                  ;dx=LCR
  1052.     mov bl,bd_bit_len       ;bx=bit length in baud divisor units
  1053.     xor bh,bh
  1054.     call SetTxBaudDiv_bx    ;set baud generator to 1 bit len.
  1055.     dec dx                  ;dx=IIR
  1056.     mov ax,tx_head          ;initialize count down
  1057.     mov TxCountDown,ax      ;with Tx head len
  1058.     mov ServTx,offset ServTxHead    ;Transmitter state is "head"
  1059.     ret
  1060. ClearStat:
  1061.     call ClearDataStat
  1062.     ret
  1063.  
  1064.     even
  1065. Tx8bit     dw 0
  1066. NextTxBit  db 0
  1067.  
  1068. ServTxHead:
  1069.     add dx,bx               ;dx=MCR
  1070.     in al,dx                ;flip DTR
  1071.     xor al,1
  1072.     out dx,al
  1073.     sub dx,bx               ;dx=IIR
  1074.     dec TxCountDown
  1075.     jnz ServTxHead_ret
  1076.       mov ServTx,offset ServTxData  ;enter data phase
  1077.       jmp short ReadNext8bit
  1078. ServTxHead_ret:
  1079.     ret
  1080.  
  1081. ServTxData:
  1082.     add dx,bx               ;dx=MCR
  1083.     in al,dx                ;flip DTR if next bit is 1
  1084.     xor al,NextTxBit
  1085.     out dx,al
  1086.     sub dx,bx               ;dx=IIR
  1087.     mov ax,Tx8bit           ;save bit counter and 8bit buffer
  1088.     dec ah                  ;decrement bit counter
  1089.     jz ReadNext8bit         ;jump if zero
  1090.     ror al,1                ;rotate 8bit buffer
  1091.     mov Tx8Bit,ax           ;save it
  1092.     and al,1                ;extract lowest bit
  1093.     mov NextTxBit,al        ;save it
  1094.     ret
  1095. ReadNext8bit:
  1096.     call ReadTx8bit         ;Read next 8 bits
  1097.     jnc TxStartTail         ;jump if buffer empty
  1098.     mov ah,8                ;bit counter = 8
  1099.     not al                  ;invert data bits
  1100.     mov Tx8Bit,ax           ;save bit counter and 8bit buffer
  1101.     and al,1                ;leave lowest bit only
  1102.     mov NextTxBit,al
  1103.     ret
  1104. TxStartTail:
  1105.     mov ax,tx_tail          ;load count down with tx tail length
  1106.     mov TxCountDown,ax
  1107.     mov ServTx,offset ServTxTail
  1108.     ret
  1109.  
  1110. ServTxTail:                     ;bx=2 at entry
  1111.     add dx,bx               ;dx=MCR
  1112.     in al,dx                ;flip DTR
  1113.     xor al,1
  1114.     out dx,al
  1115.     sub dx,bx               ;dx=IIR
  1116.     dec TxCountDown
  1117.     jnz ServTxTail_ret
  1118.       add dx,bx             ;dx=MCR
  1119.       in al,dx
  1120.       or al,1               ;set DTR high
  1121.       and al,0FDh           ;and RTS low
  1122.       out dx,al
  1123.       dec dx                ;dx=LCR
  1124.       mov bx,bd_slot_time   ;set TxEmpty interrupt rate to slot time
  1125.       call SetTxBaudDiv_bx
  1126.       dec dx                ;dx=IIR
  1127.       mov ServTx,offset ServTxIdle
  1128.       call ClearDataStat    ;clear statistics for DCD
  1129. ServTxTail_ret:
  1130.     ret
  1131.  
  1132. SetTxBaudDiv_bx:        ;input: bx=divisor value, dx=LCR
  1133.     mov al,81h      ;enable divisor access
  1134.     out dx,al
  1135.     sub dx,3        ;dx=DLL
  1136.     mov ax,bx       ;write in new baud rate
  1137.     out dx,al
  1138.     inc dx
  1139.     xchg al,ah
  1140.     out dx,al
  1141.     add dx,2        ;dx=LCR again
  1142.     mov al,01h      ;disable divisor access
  1143.     out dx,al
  1144.     ret             ;output: dx=LCR, ax,bx modified
  1145.  
  1146. RandomWord dw 079BDh            ;random number for p-persistance algorithm
  1147.  
  1148. Randomize:                      ;make new random number
  1149.     mov cx,dx               ;save dx
  1150.     xor al,al               ;read system timer count
  1151.     out 43h,al
  1152.     in al,40h               ;low byte
  1153.     xchg al,ah
  1154.     in al,40h               ;high byte
  1155.     xchg al,ah              ;timer value in ax now
  1156.     add ax,RandomWord       ;add previous random word
  1157.     mov bx,65521            ;multiply by 65521
  1158.     mul bx                  ;dx:ax = ax * bx
  1159.     xor ax,dx
  1160.     mov RandomWord,ax       ;save random word
  1161.     mov dx,cx               ;restore dx
  1162.     ret                     ;ax,bx,cx modified
  1163.  
  1164. TxPTTDecision:                  ;this routine decides whether to push PTT now
  1165.                 ;input: dx=IIR
  1166.     mov al,carrier_sense
  1167.     and al,al               ;Full duplex ?
  1168.     jz PushPTT              ;jump if so
  1169.     call sense_carrier      ;Is somebody else transmiting ?
  1170.     jc DontPushPTT          ;jump if so
  1171.                 ;now comes the p-persistance...
  1172.     mov ax,RandomWord       ;ax=pseudo-random word
  1173.     xor ah,al               ;ah=pseudo-random byte
  1174.     mov al,persistance      ;al=persistance
  1175.     cmp ah,al               ;carry when ah<persistance
  1176.     ret
  1177.  
  1178. PushPTT:
  1179.     stc     ;carry:=1 => push PTT
  1180.     ret
  1181.  
  1182. DontPushPTT:
  1183.     clc     ;carry:=0 => do not push PTT
  1184.     ret
  1185.  
  1186. sense_carrier:          ;input: al=carrier mode, dx=MCR
  1187.             ;output: carry=carrier state
  1188.  
  1189.     cmp al,1                ;sense DCD line ?
  1190.     jz sense_DCD
  1191.     cmp al,2                ;sense data transition
  1192.     jz sense_DataTrans
  1193.     cmp al,3                ;be more clever ?
  1194.     jz sense_data
  1195.     clc                     ;otherwise just say carrier=false
  1196.     ret
  1197.  
  1198. sense_DCD:
  1199.     add dx,4        ;dx=MSR
  1200.     in al,dx        ;read MSR
  1201.     sub dx,4        ;dx=IIR
  1202.     rcl al,1        ;carry=DCD state
  1203.     ret
  1204.  
  1205. sense_DataTrans:
  1206.     xor ax,ax               ;at least one data transition
  1207.     cmp ax,DataTransCount   ;since previous time slot ?
  1208.     ret                     ;carry=1 if so
  1209.  
  1210. sense_data:
  1211.     mov bx,DataTransCount
  1212.     cmp bx,2                ;more than 1 transitions counted ?
  1213.     jc few_trans            ;jump if not
  1214.     mov cx,dx               ;save dx
  1215.     mov ax,PeriodDevSum     ;compute sum/count that is the average
  1216.     mov dx,PeriodDevSum+2
  1217.     div bx                  ;ax=periodDevSum div DataTransCount
  1218.     cmp ax,cl_dcd_thres     ;is the average bigger than dcd_threshold ?
  1219.     mov dx,cx               ;restore dx
  1220.     ret                     ;carry=0 (no carrier) if so
  1221. few_trans:
  1222.     clc             ;say carrier=false if there were only few transitions
  1223.     ret
  1224.  
  1225.         even
  1226. DataTransCount  dw 0            ;count input signal transitions
  1227. PeriodDevSum    dw 0,0          ;sums period deviations from round bit lenghts
  1228. prev_period     dw 0
  1229.  
  1230. ClearDataStat:                  ;clear statistics for DCD
  1231.     xor ax,ax
  1232.     mov DataTransCount,ax
  1233.     mov PeriodDevSum,ax
  1234.     mov PeriodDevSum+2,ax
  1235.     ret
  1236. UpdateDataStat:                 ;input: ax=period
  1237.     inc DataTransCount      ;increment data transition counter
  1238.     mov bl,carrier_sense    ;check carrier mode
  1239.     cmp bl,3                ;execute the rest only if carrier mode is 3
  1240.     jnz UpdateDataStat_ret
  1241.     push dx
  1242.     xor dx,dx
  1243.     xchg ax,prev_period     ;prev_period=period;
  1244.     add ax,prev_period      ;ax=period+prev_period
  1245.     add ax,cl_bit_len_2     ;period+=cl_bit_len/2
  1246.     mov bx,cl_bit_len
  1247.     div bx                  ;dx=(period+cl_bit_len_2) div cl_bit_len
  1248.     shr bx,1                ;bx=cl_bit_len/2
  1249.     sub dx,bx               ;dx-=cl_bit_len/2
  1250.     jnc UpdateDevSum        ;if result negative
  1251.       neg dx                ;then negate it (we need absolute value)
  1252. UpdateDevSum:
  1253.     xor ax,ax               ;add dx to PeriodDevSum
  1254.     add PeriodDevSum,dx     ;PeriodDevSum sums deviation of periods
  1255.     adc PeriodDevSum+2,ax   ;from multiple bit lengths
  1256.                 ;for DCD decision
  1257.     pop dx
  1258. UpdateDataStat_ret:
  1259.     ret             ;ax,bx modified
  1260. ;==============================================================
  1261.  
  1262.     even
  1263. TxBufferLen equ 4095             ;Tx buffer length in bytes - must be 2^n-1
  1264. TxBuffer db TxBufferLen+1 dup(0) ;Tx buffer storage
  1265.                  ;this buffer is filled by send_pkt routine
  1266.                  ;and flushed by TxEmpty service routine
  1267.                  ;when tx is in data phase
  1268.                  ;buffer pointers
  1269. TxReadPtr  dw 0         ;points to next byte to read
  1270. TxWritePtr dw 0         ;points to successor of the last valid byte
  1271. TxBlockPtr dw 0         ;Temporary pointer while writing in data block
  1272.             ;this is to ensure that only complete blocks
  1273.             ;will be transmitted
  1274.  
  1275. WriteTx8bit:            ;input: al=byte to write
  1276.     push bx
  1277.     mov bx,TxBlockPtr       ;load block end pointer
  1278.     mov [TxBuffer+bx],al    ;store byte
  1279.     inc bx                  ;increase pointer
  1280.     and bx,TxBufferLen      ;and flip it around
  1281.     cmp bx,TxReadPtr        ;same as read ptr ?
  1282.     jz WriteTx_ret          ;jump if so
  1283.     mov TxBlockPtr,bx
  1284. WriteTx_ret:
  1285.     pop bx
  1286.     ret             ;on exit: Z set = buffer full
  1287.             ;registers unchanged
  1288.  
  1289. ValidateTxBlock:
  1290.     push bx
  1291.     mov bx,TxBlockPtr       ;make write pointer same as block pointer
  1292.     mov TxWritePtr,bx
  1293.     pop bx
  1294.     ret             ;registers unchanged
  1295.  
  1296. CancelTxBlock:
  1297.     push bx
  1298.     mov bx,TxWritePtr       ;make block pointer same as write pointer
  1299.     mov TxBlockPtr,bx
  1300.     pop bx
  1301.     ret             ;registers unchanged
  1302.  
  1303. ReadTx8bit:
  1304.     mov bx,TxReadPtr        ;load read pointer
  1305.     cmp bx,TxWritePtr       ;same as write pointer ?
  1306.     jz ReadTx_ret           ;jump if so
  1307.       mov al,[TxBuffer+bx]  ;read data into AL
  1308.       inc bx                ;increment the read pointer
  1309.       and bx,TxBufferLen    ;and flip it around
  1310.       mov TxReadPtr,bx      ;save it
  1311.       stc                   ;set carry
  1312. ReadTx_ret:
  1313.     ret     ;if carry is 0 => buffer was empty
  1314.         ;bx is modified but this does not matter really
  1315.  
  1316.     even
  1317. Tx8bitBuffer dw 8000h
  1318.  
  1319. AddTxBit:                       ;input: bh=8bit reg., bl=1s counter
  1320.     inc bl                  ;increment 1s counter
  1321.     jc AddTxBit_1
  1322.       mov bl,0              ;clear counter when 0 bit
  1323. AddTxBit_1:
  1324.     rcr bh,1                ;shift the bit into 8bit reg.
  1325.     jnc AddTxBit_ret
  1326.       mov al,bh             ;write reg. into buffer
  1327.       call WriteTx8bit
  1328.       jz AddTxBit_err
  1329.       mov bh,80h
  1330. AddTxBit_ret:
  1331.     clc                     ;clear carry => no problems
  1332.     ret
  1333. AddTxBit_err:
  1334.     stc                     ;set carry => buffer overflow
  1335.     ret
  1336.  
  1337. TxFlush8bit:
  1338.     push ax
  1339.     mov ax,Tx8bitBuffer
  1340.     mov al,ah
  1341.     and al,al
  1342.     jz TxFlush_ret
  1343.     clc
  1344. TxFlush_l:
  1345.     rcr al,1
  1346.     jnc TxFlush_l
  1347.     call WriteTx8bit
  1348. TxFlush_ret:
  1349.     mov ax,8000h
  1350.     mov Tx8bitBuffer,ax
  1351.     pop ax
  1352.     ret             ;output: Z=1 means tx buffer overflow
  1353.  
  1354. AddTxByteDirect:        ;input: ah=byte to add
  1355.     push ax
  1356.     push bx
  1357.     push cx
  1358.     mov bx,Tx8bitBuffer
  1359.     mov cx,8
  1360. AddNextBit:
  1361.       ror ah,1
  1362.       call AddTxBit
  1363.       jc AddTxByte_ret
  1364.     loop AddNextBit
  1365. AddTxByte_ret:
  1366.     mov Tx8bitBuffer,bx
  1367.     pop cx
  1368.     pop bx
  1369.     pop ax
  1370.     ret             ;output: carry=1 if buffer overflow
  1371.  
  1372. AddTxByteStuffing:              ;input: ah=byte to append _with_ bit stuffing
  1373.     push ax
  1374.     push bx
  1375.     push cx
  1376.     mov bx,Tx8bitBuffer
  1377.     mov cx,8
  1378. AddNextBitS:                    ;loop over bits
  1379.       ror ah,1              ;copy next bit to carry flag
  1380.       call AddTxBit
  1381.       jc AddTxByteS_ret
  1382.       cmp bl,5
  1383.       jc AddTxByteS_l
  1384.         clc                 ;if more than 5 1s in a row
  1385.         call AddTxBit       ;append an extra 0 bit
  1386.         jc AddTxByteS_ret   ;jump if buffer overflow
  1387. AddTxByteS_l:
  1388.     loop AddNextBitS
  1389.     clc
  1390. AddTxByteS_ret:
  1391.     mov Tx8bitBuffer,bx
  1392.     pop cx
  1393.     pop bx
  1394.     pop ax
  1395.     ret                     ;output: carry=1 means buffer overflow
  1396.  
  1397.  
  1398. TxBufferEmpty:
  1399.     mov bx,TxReadPtr
  1400.     cmp bx,TxWritePtr
  1401.     ret     ;Z=1 means buffer is empty
  1402.         ;bx is modified
  1403.  
  1404.     even                            ;align buffer to word boudary
  1405. RxBufferLen equ 1023                    ;in words, must be 2^n-1
  1406. RxBuffer   dw RxBufferLen+1 dup(0)      ;Rx buffer storing periods between CTS transition
  1407.                     ;this buffer is filled by CTS transition
  1408.                     ;interrupt routine and flushed
  1409.                     ;by system timer service routine
  1410. RxReadPtr  dw 0                         ;read pointer
  1411. RxWritePtr dw 0                         ;write pointer
  1412.  
  1413. StoreRxPeriod:                  ;must not modify dx,cx
  1414.     mov bx,RxWritePtr       ;load store pointer
  1415.     mov [RxBuffer+bx],ax    ;store the period
  1416.     add bx,2                ;increment the pointer
  1417.     and bx,2*RxBufferLen    ;turn it around if needed
  1418.     mov RxWritePtr,bx       ;save it
  1419.     cmp bx,RxReadPtr        ;same as ReadPtr ?
  1420.     jnz RxStore_ret         ;jump if not
  1421.       mov bx,RxReadPtr
  1422.       add bx,2              ;increment the read pointer
  1423.       and bx,2*RxBufferLen  ;and turn it around
  1424.       mov RxReadPtr,bx
  1425. RxStore_ret:
  1426.     ret
  1427. ;the above routine discards the oldest period when the buffer overflows
  1428.  
  1429. ReadRxPeriod:                   ;modifies only ax
  1430.     push bx
  1431.     mov bx,RxReadPtr        ;load read pointer
  1432.     cmp bx,RxWritePtr       ;same as write pointer ?
  1433.     jz RxRead_ret           ;jump if so
  1434.       mov ax,[RxBuffer+bx]  ;read the period
  1435.       add bx,2              ;increase the pointer
  1436.       and bx,2*RxBufferLen  ;turn it around
  1437.       mov RxReadPtr,bx      ;save it
  1438.       stc                   ;set carry => data is in ax
  1439. RxRead_ret:
  1440.     pop bx
  1441.     ret                     ;if carry is 0 => then buffer was empty
  1442.                 ;otherwise ax = period
  1443.  
  1444. ;==============================================================
  1445. ;CRC computation table and routine
  1446.  
  1447.     even
  1448. CRCtable dw         0,  4489,  8978, 12955, 17956, 22445, 25910, 29887
  1449.      dw     35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735
  1450.      dw      4225,   264, 13203,  8730, 22181, 18220, 30135, 25662
  1451.      dw     40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510
  1452.      dw      8450, 12427,   528,  5017, 26406, 30383, 17460, 21949
  1453.      dw     44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797
  1454.      dw     12675,  8202,  4753,   792, 30631, 26158, 21685, 17724
  1455.      dw     48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572
  1456.      dw     16900, 21389, 24854, 28831,  1056,  5545, 10034, 14011
  1457.      dw     52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859
  1458.      dw     21125, 17164, 29079, 24606,  5281,  1320, 14259,  9786
  1459.      dw     57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634
  1460.      dw     25350, 29327, 16404, 20893,  9506, 13483,  1584,  6073
  1461.      dw     61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921
  1462.      dw     29575, 25102, 20629, 16668, 13731,  9258,  5809,  1848
  1463.      dw     65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696
  1464.      dw     33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623
  1465.      dw      2112,  6601, 11090, 15067, 20068, 24557, 28022, 31999
  1466.      dw     38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398
  1467.      dw      6337,  2376, 15315, 10842, 24293, 20332, 32247, 27774
  1468.      dw     42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685
  1469.      dw     10562, 14539,  2640,  7129, 28518, 32495, 19572, 24061
  1470.      dw     46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460
  1471.      dw     14787, 10314,  6865,  2904, 32743, 28270, 23797, 19836
  1472.      dw     50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747
  1473.      dw     19012, 23501, 26966, 30943,  3168,  7657, 12146, 16123
  1474.      dw     54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522
  1475.      dw     23237, 19276, 31191, 26718,  7393,  3432, 16371, 11898
  1476.      dw     59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809
  1477.      dw     27462, 31439, 18516, 23005, 11618, 15595,  3696,  8185
  1478.      dw     63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584
  1479.      dw     31687, 27214, 22741, 18780, 15843, 11370,  7921,  3960
  1480.  
  1481. CRCpass:                ;input: dx=partial CRC
  1482.     push bx         ;       ah=char to process
  1483.     push ax
  1484.     xor dl,ah
  1485.     mov bl,dl
  1486.     xor bh,bh
  1487.     add bx,bx
  1488.     mov dl,dh
  1489.     xor dh,dh
  1490.     xor dx,[CRCtable+bx]
  1491.     pop ax
  1492.     pop bx
  1493.     ret
  1494.  
  1495. ;CRC in DX must be initialized with 0FFFFh
  1496. ;and inverted after passing through all characters
  1497. ;==============================================================
  1498. ;here is the installation and de-installation code
  1499.  
  1500. deinstall_driver:
  1501.     mov al,packet_int_no    ;disconnect DRVR_ISR
  1502.     mov ah,25h
  1503.     push ds
  1504.     lds dx,dword ptr old_packet_int
  1505.     int 21h
  1506.     pop ds
  1507.  
  1508.     call restore_COM        ;restore COM port state
  1509.                 ;and interrupt vector(s)
  1510.     call Restore_Timer      ;restore Timer interrupt
  1511.  
  1512.     push cs                 ;free memory
  1513.     pop es
  1514.     mov ah,49h
  1515.     int 21h
  1516.     ret
  1517.  
  1518. end_resident:   ;all code after this point will not stay resident
  1519.         ;after the installation is done.
  1520.  
  1521. Int_is_busy:            ;there is already a packet driver installed
  1522.             ;at specified software interrupt
  1523.     call Print_following_string
  1524.     db 'There is already a packet driver at interrupt 0x',0
  1525.     mov dl,packet_int_no
  1526.     call Print_DL_hex
  1527. DoNotInstall:
  1528.     call Print_following_string
  1529.     db 13,10,BOLD,'AX.25 driver has _not_ been installed',NORM,13,10,0
  1530.     mov ax,4C00h                    ;terminate but don't stay resident
  1531.     int 21h
  1532.     ret
  1533.  
  1534. BadUsage:
  1535.     call Print_following_string
  1536.     db 13,10,0
  1537.     mov bx,offset Msg_usage
  1538.     call Print_BX_string
  1539.     jmp short DoNotInstall
  1540.  
  1541. install_driver:
  1542.  
  1543.     call Print_following_string
  1544.     db NORM
  1545.     db 'AX.25 packet driver for RS232 port by Pawel Jalocha',13,10
  1546.     db 'Version of 4th January 1993',13,10
  1547.     db 'Free licence is granted for radio _amateurs_ only',13,10,0
  1548.  
  1549.     call ReadOptions
  1550.     jnc DoPrintParam
  1551.     jmp BadUsage
  1552. DoPrintParam:
  1553.     call PrintParameters
  1554.     call CheckParameters
  1555.     jnc DoComputeSec
  1556.     jmp DoNotInstall
  1557. DoComputeSec:
  1558.     call ComputeSecondaryPar
  1559.  
  1560.     push es
  1561.  
  1562.     mov al,packet_int_no    ;save int vector
  1563.     mov ah,35h
  1564.     int 21h
  1565.     mov old_packet_int,bx
  1566.     mov ax,es
  1567.     mov old_packet_int+2,ax
  1568.  
  1569.     add bx,3                ;check if there is already a packet driver
  1570.     mov di,bx
  1571.     mov si,offset DRVR_ISR +3
  1572.     mov cx,9
  1573. cmp_char: mov al,[si]
  1574.       inc si
  1575.       cmp al,es:[di]
  1576.       jne Int_is_free
  1577.       inc di
  1578.     loop cmp_char
  1579.     jmp Int_is_busy
  1580. Int_is_free:
  1581.  
  1582.     mov al,packet_int_no    ;put a new one in place
  1583.     mov ah,25h
  1584.     mov dx,offset DRVR_ISR
  1585.     int 21h                 ;ds must be equal to cs here
  1586.  
  1587.     mov es,phd_environ      ;release our environment
  1588.     mov ah,49h
  1589.     int 21h
  1590.  
  1591.     pop es
  1592.  
  1593. ;initialize COM port and interrupt vector(s)
  1594.     call initialize_COM
  1595. ;initialize Timer routine
  1596.     call Initialize_Timer
  1597.  
  1598.     call Print_following_string
  1599.     db BOLD,'AX.25 driver is now installed and initialized',NORM,13,10,0
  1600.  
  1601. ;make the code resident
  1602.     mov ax,3100h
  1603.     mov dx,offset end_resident + 0Fh
  1604.     mov cl,4
  1605.     shr dx,cl
  1606.     int 21h
  1607.     ret
  1608.  
  1609. Msg_usage:
  1610.     db 'ax25 options: (default values in [])',13,10
  1611.     db BOLD,'-?',NORM,' prints this help message',13,10
  1612.     db BOLD,'-i',NORM,'<int_no>(hex) software interrupt number [60]',13,10
  1613.     db BOLD,'-I',NORM,'<irq>(hex)[p][s] COM IRQ number 2..7 [4]',13,10
  1614.     db BOLD,'-B',NORM,'<base>(hex)      COM base address 0..3ff [3f8]',13,10
  1615.     db BOLD,'-b',NORM,'<bit rate>(dec) [1200]',13,10
  1616.     db BOLD,'-c',NORM,'<carrier mode> possible choices are:',13,10
  1617.     db '  -cf = full duplex (do not care about channel busy - just transmit)',13,10
  1618.     db '  -cc = sense DCD modem line',13,10
  1619.     db '  -ct = sense data transitions [default] (hardware DCD)',13,10
  1620.     db '  -cd = deliver carrier signal from data analysis (software DCD)',13,10
  1621.     db BOLD,'-T',NORM,'<threshold>(dec)  software DCD threshold 0..100 [50]',13,10
  1622.     db BOLD,'-s',NORM,'<slot time>(dec) slot time in data bits [120]',13,10
  1623.     db BOLD,'-p',NORM,'<peristance>(dec) persistance/255 [64]',13,10
  1624.     db BOLD,'-h',NORM,'<tx head>(dec)  Transmitter head in data bit units [240]',13,10
  1625.     db BOLD,'-t',NORM,'<tx tail>(dec)  transmitter tail in data bit units [24]',13,10
  1626.     db BOLD,'-S',NORM,' sound effects (beeps for every received frame)',13,10
  1627.     db 0
  1628.  
  1629. ;==============================================================
  1630. ;Here are some routine for printing numbers and strings
  1631.  
  1632. print_following_string: ;prints string following the call - modifies bx !
  1633.             ;string must following "call print_following_string"
  1634.             ;_must_ end with NULL character !
  1635.     pop bx          ;pop return address from the stack
  1636.     push ax         ;so we know where the char. string is
  1637.     push dx
  1638. print_next_char:
  1639.       mov dl,cs:[bx]        ;load next character
  1640.       inc bx
  1641.       and dl,dl             ;NULL char ?
  1642.       jz string_end         ;exit this loop if so
  1643.       mov ah,2              ;otherwise print it
  1644.       int 21h
  1645.     jmp short print_next_char
  1646. string_end:
  1647.     pop dx
  1648.     pop ax
  1649.     push bx                 ;push new return address on stack
  1650.     ret
  1651. print_BX_string:        ;prints string addressed by ds:bx
  1652.     push ax         ;the string must be terminated by NULL char
  1653.     push bx
  1654.     push dx
  1655. print_next_BX_char:             ;loop over characters
  1656.       mov dl,[bx]           ;read next character
  1657.       inc bx
  1658.       and dl,dl             ;NULL char ?
  1659.       jz BX_string_end      ;jump if so
  1660.       mov ah,2              ;otherwise print it
  1661.       int 21h
  1662.     jmp short print_next_BX_char
  1663. BX_string_end:
  1664.     pop dx
  1665.     pop bx
  1666.     pop ax
  1667.     ret
  1668.  
  1669. print_DL_hex:           ;prints in hex byte stored in DL
  1670.     push ax
  1671.     push cx
  1672.     mov cl,4
  1673.     jmp short print_low_byte
  1674. print_DX_hex:           ;prints in hex word stored in DX
  1675.     push ax
  1676.     push cx
  1677.     mov cl,4
  1678.     mov al,dh
  1679.     rol al,cl
  1680.     call print_hex_digit
  1681.     rol al,cl
  1682.     call print_hex_digit
  1683. print_low_byte:
  1684.     mov al,dl
  1685.     rol al,cl
  1686.     call print_hex_digit
  1687.     rol al,cl
  1688.     call print_hex_digit
  1689.     pop cx
  1690.     pop ax
  1691.     ret
  1692.  
  1693. print_hex_digit:        ;prints hex digit stored in AL
  1694.     push ax
  1695.     push dx
  1696.     and al,0Fh
  1697.     cmp al,10
  1698.     jc add_0
  1699.     add al,'a'-'0'-10
  1700. add_0:  add al,'0'
  1701.     mov dl,al
  1702.     mov ah,2
  1703.     int 21h
  1704.     pop dx
  1705.     pop ax
  1706.     ret
  1707.  
  1708. print_DX_dec:           ;print in decimal word stored in DX
  1709.     push ax
  1710.     push bx
  1711.     push cx
  1712.     push dx
  1713.     mov bx,10
  1714.     xor cx,cx
  1715.     mov ax,dx
  1716. calc_next_dig:
  1717.     xor dx,dx
  1718.     div bx
  1719.     push dx
  1720.     inc cx
  1721.     and ax,ax
  1722.     jnz calc_next_dig
  1723. print_next_dig:
  1724.     pop ax
  1725.     call print_dec_digit
  1726.     loop print_next_dig
  1727.     pop dx
  1728.     pop cx
  1729.     pop bx
  1730.     pop ax
  1731.     ret
  1732. print_dec_digit:        ;prints in dec digit stored in AL
  1733.     push ax
  1734.     push dx
  1735.     add al,48
  1736.     mov dl,al
  1737.     mov ah,2
  1738.     int 21h
  1739.     pop dx
  1740.     pop ax
  1741.     ret
  1742.  
  1743. ;==============================================================
  1744. ;Routines to interprete input
  1745.  
  1746. SkipBlanks:             ;mov to first non-SPACE nor TAB char.
  1747.             ;ds:[bx] = string address
  1748. SkipThisChar:
  1749.     mov al,[bx]     ;load next char.
  1750.     inc bx          ;increment pointer
  1751.     cmp al,' '      ;space ?
  1752.     jz SkipThisChar ;jump if SPACE
  1753.     cmp al,9        ;TAB ?
  1754.     jz SkipThisChar ;jump if TAB
  1755.     dec bx          ;if not SPACE nor TAB move pointer back
  1756.     ret             ;ds:bx=address of non-blank character
  1757.             ;al=this character
  1758.  
  1759. ReadOptions:
  1760.     push ax
  1761.     push bx
  1762.     push cx
  1763.     push dx
  1764.     mov bx,81h              ;load offset to command line arguments
  1765. NextOption:
  1766.     call SkipBlanks         ;al=next non-blank char
  1767.     cmp al,13               ;carriage return ?
  1768.     jz ReadOptions_ret
  1769.     cmp al,'-'              ;minus sign ?
  1770.     jz InterpreteOption     ;if so go and interprete following chars
  1771. PrintOptionUsage:               ;otherwise set carry to indicate a problem
  1772. ;       mov bx,offset Msg_usage
  1773. ;       call Print_BX_string
  1774. ReadOptions_err:
  1775.     stc
  1776. ReadOptions_ret:
  1777.     pop dx
  1778.     pop cx
  1779.     pop bx
  1780.     pop ax
  1781.     ret             ;carry=1 => results are _not_ valid
  1782.  
  1783. InterpreteOption:       ;interprete an option
  1784.             ;ds:bx=address of '-' char.
  1785.     inc bx
  1786.     mov al,[bx]     ;load char after '-'
  1787.     inc bx          ;move pointer futher
  1788.     cmp al,'?'      ;question mark ?
  1789.     jz PrintOptionUsage
  1790.     cmp al,'B'      ;B ? (COM base address)
  1791.     jz Opt_base
  1792.     cmp al,'i'
  1793.     jz Opt_interrupt
  1794.     cmp al,'b'
  1795.     jz Opt_baud
  1796.     cmp al,'s'
  1797.     jz Opt_slot
  1798.     cmp al,'S'
  1799.     jz Opt_sound
  1800.     cmp al,'p'
  1801.     jz Opt_persistance
  1802.     cmp al,'h'
  1803.     jz Opt_head
  1804.     cmp al,'t'
  1805.     jz Opt_tail
  1806.     cmp al,'T'
  1807.     jz Opt_DCD_thres
  1808.     cmp al,'I'
  1809.     jz Opt_irq
  1810.     cmp al,'c'
  1811.     jz Opt_carrier
  1812.  
  1813.     call UnknownOption      ;if non of the above says the option
  1814.                 ;was not recognized
  1815.     jmp ReadOptions_err
  1816.  
  1817. Opt_base:                       ;COM base option
  1818.     call ReadHexNumber      ;read hex number following -B
  1819.     mov com_base,dx         ;save it in com_base
  1820.     jmp short NextOption
  1821. Opt_interrupt:
  1822.     call ReadHexNumber
  1823.     mov packet_int_no,dl
  1824.     jmp short NextOption
  1825. Opt_baud:
  1826.     call ReadDecNumber
  1827.     mov bit_rate,dx
  1828.     jmp short NextOption
  1829. Opt_slot:
  1830.     call ReadDecNumber
  1831.     mov slot_time,dl
  1832.     jmp short NextOption
  1833. Opt_sound:
  1834.     mov dl,0FFh
  1835.     mov sound,dl
  1836.     jmp short NextOption
  1837. Opt_persistance:
  1838.     call ReadDecNumber
  1839.     mov persistance,dl
  1840.     jmp short NextOption
  1841. Opt_head:
  1842.     call ReadDecNumber
  1843.     mov tx_head,dx
  1844.     jmp NextOption
  1845. Opt_tail:
  1846.     call ReadDecNumber
  1847.     mov tx_tail,dx
  1848.     jmp NextOption
  1849. Opt_DCD_thres:
  1850.     call ReadDecNumber
  1851.     mov dcd_thres,dx
  1852.     jmp NextOption
  1853.  
  1854.  
  1855. Opt_irq:                        ;COM irq option
  1856.     call ReadHexNumber      ;read hex number following -I
  1857.     mov com_irq,dl          ;save it in com_irq
  1858. irq_sub_opt:                    ;irq sub-options
  1859.     cmp al,'p'              ;is there 'p' after the irq number ?
  1860.     jz irq_prio
  1861.     cmp al,'s'              ;is there 's' after the irq number ?
  1862.     jz irq_share
  1863.     jmp NextOption
  1864.  
  1865. irq_prio:
  1866.     mov al,0FFh
  1867.     mov com_irq_prio,al
  1868.     inc bx
  1869.     mov al,[bx]
  1870.     jmp short irq_sub_opt
  1871.  
  1872. irq_share:
  1873.     mov al,0FFh
  1874.     mov com_irq_share,al
  1875.     inc bx
  1876.     mov al,[bx]
  1877.     jmp short irq_sub_opt
  1878.  
  1879. Opt_carrier:            ;carrier detect method
  1880.     mov al,[bx]
  1881.     cmp al,'f'
  1882.     jz Carr_0
  1883.     cmp al,'c'
  1884.     jz Carr_1
  1885.     cmp al,'t'
  1886.     jz Carr_2
  1887.     cmp al,'d'
  1888.     jz Carr_3
  1889.     call UnknownCarrierOpt
  1890.     jmp ReadOptions_err
  1891. Opt_carr_end:
  1892.     inc bx
  1893.     mov Carrier_sense,al
  1894.     jmp NextOption
  1895.  
  1896. Carr_0: mov al,0
  1897.     jmp short Opt_carr_end
  1898. Carr_1: mov al,1
  1899.     jmp short Opt_carr_end
  1900. Carr_2: mov al,2
  1901.     jmp short Opt_carr_end
  1902. Carr_3: mov al,3
  1903.     jmp short Opt_carr_end
  1904.  
  1905. UnknownCarrierOpt:      ;say the carrier option was not recognized
  1906.     push bx
  1907.     call Print_following_string
  1908.     db BOLD,BLINK,'Unknown option: -c',0
  1909.     mov dl,al
  1910.     mov ah,2
  1911.     int 21h
  1912.     call Print_following_string
  1913.     db NORM,13,10,0
  1914.     pop bx
  1915.     ret
  1916.  
  1917. UnknownOption:          ;say the option not recognized
  1918.     push bx
  1919.     call Print_following_string
  1920.     db BOLD,BLINK,'Unknown option: -',0
  1921.     mov dl,al
  1922.     mov ah,2
  1923.     int 21h
  1924.     call Print_following_string
  1925.     db NORM,13,10,0
  1926.     pop bx
  1927.     ret
  1928.  
  1929. Param_OK db 0           ;"parameters are OK" flag
  1930.  
  1931. CheckParameters:
  1932.     push bx
  1933.     push ax
  1934.     mov al,1
  1935.     mov Param_OK,al
  1936.  
  1937.     call Print_following_string
  1938.     db BOLD,BLINK,0
  1939.  
  1940. ;       mov al,packet_int_no
  1941. ;       cmp al,60h
  1942. ;       jnc int_no_OK
  1943. ;         call Print_following_string
  1944. ;         db 'Packet interrupt number is below 0x60',13,10,0
  1945. ;         xor al,al
  1946. ;         mov Param_OK,al
  1947. ;int_no_OK:
  1948.  
  1949.     mov ax,bit_rate         ;check bit rate
  1950.     cmp ax,300              ;must be >= 300
  1951.     jnc baud_upp
  1952.       call Print_following_string
  1953.       db 'Bauds below 300 bps not supported',13,10,0
  1954.       xor al,al
  1955.       mov Param_OK,al
  1956. baud_upp:
  1957.     cmp ax,14400+1          ;but as well <= 14400
  1958.     jc baud_OK
  1959.       call Print_following_string
  1960.       db 'Bauds above 14400 bps not supported',13,10,0
  1961.       xor al,al
  1962.       mov Param_OK,al
  1963. baud_OK:
  1964.     mov ax,tx_head          ;tx head (TxDelay)
  1965.     cmp ax,8                ;must be >= 8 bits
  1966.     jnc tx_head_OK
  1967.       call Print_following_string
  1968.       db 'Tx head should be at least 8 bits',13,10,0
  1969.       xor al,al
  1970.       mov Param_OK,al
  1971. tx_head_OK:
  1972.     mov ax,tx_tail          ;tx tail
  1973.     cmp ax,8                ;must be >= 8 bits
  1974.     jnc tx_tail_OK
  1975.       call Print_following_string
  1976.       db 'Tx tail should be at least 8 bits',13,10,0
  1977.       xor al,al
  1978.       mov Param_OK,al
  1979. tx_tail_OK:
  1980.     mov al,slot_time        ;slot time
  1981.     cmp al,8                ;must be >= 8 bits
  1982.     jnc slot_time_OK
  1983.       call Print_following_string
  1984.       db 'Slot time should be at least 8 bits',13,10,0
  1985.       xor al,al
  1986.       mov Param_OK,al
  1987. slot_time_OK:
  1988.     mov ax,com_base         ;com base
  1989.     cmp ax,400h             ;must be < 400h
  1990.     jc com_base_OK
  1991.       call Print_following_string
  1992.       db 'COM base address should be in the range 0..3ff',13,10,0
  1993.       xor al,al
  1994.       mov Param_OK,al
  1995. com_base_OK:
  1996.     mov al,com_irq          ;com irq
  1997.     cmp al,2                ;must be > 2
  1998.     jnc com_irq_upp
  1999. wrong_irq:
  2000.       call Print_following_string
  2001.       db 'COM irq should be between 2 and 7',13,10,0
  2002.       xor al,al
  2003.       mov Param_OK,al
  2004.       jmp short com_irq_OK
  2005. com_irq_upp:
  2006.       cmp al,8              ;and < 8 as well
  2007.       jnc wrong_irq
  2008. com_irq_OK:
  2009.     mov ax,dcd_thres
  2010.     cmp ax,101
  2011.     jc dcd_thres_OK
  2012.       call Print_following_string
  2013.       db 'DCD threshold must be between 0 and 100',13,10,0
  2014.       xor al,al
  2015.       mov Param_OK,al
  2016. dcd_thres_OK:
  2017.     
  2018.     mov al,Param_OK
  2019.     shr al,1
  2020.     cmc
  2021.     jnc CheckParam_ret
  2022. ;        call Print_following_string
  2023. ;        db 'ax25 driver can not accept these parameters',7,13,10,0
  2024.     stc
  2025.     
  2026. CheckParam_ret:
  2027.     pushf                           ;save flags to preserve carry
  2028.     call Print_following_string
  2029.     db NORM,0
  2030.     popf
  2031.  
  2032.     pop bx
  2033.     pop ax
  2034.     ret
  2035.  
  2036. ComputeSecondaryPar:            ;computes secondary parameters
  2037.                 ;after the primary ones are defined
  2038.     push ax
  2039.     push bx
  2040.     push cx
  2041.     push dx
  2042.  
  2043.     mov al,1                ;take com_irq
  2044.     mov cl,com_irq
  2045.     shl al,cl               ;and compute bit mask
  2046.     mov irq_mask,al
  2047.  
  2048.                 ;compute bit length for the transmitter
  2049.                 ;in UART baud rate generator ticks
  2050.     mov ax,14400
  2051.     xor dx,dx               ;dx:ax := 14400
  2052.     mov cx,bit_rate         ;cx := bit_rate
  2053.     mov bx,cx
  2054.     shr bx,1                ;bx := bit_rate/2
  2055.     add ax,bx               ;dx:ax := 14400 + bit_rate/2
  2056.     adc dx,0
  2057.     div cx                  ;ax:=(14400 + bit_rate/2) div bit_rate
  2058.     mov bd_bit_len,al       ;save bit length
  2059.  
  2060.     mov ax,14400            ;correct bit rate to a round value
  2061.     xor dx,dx               ;dx:ax := 14400
  2062.     mov cl,bd_bit_len
  2063.     xor ch,ch               ;cx := bd_bit_len
  2064.     div cx                  ;ax := 14400 div bd_bit_len
  2065.     xchg ax,bit_rate        ;bit_rate <-> ax
  2066.     cmp ax,bit_rate         ;rates are indeed different ?
  2067.     jz Compute_cl_bit_len   ;jump if not
  2068.       call Print_following_string   ;notify about speed adjustments
  2069.       db 'Bit rate adjusted to ',0
  2070.       mov dx,bit_rate
  2071.       call Print_DX_dec
  2072.       call Print_following_string
  2073.       db ' bps',13,10,0
  2074.  
  2075. Compute_cl_bit_len:             ;compute bit length for the receiver
  2076.                 ;in timer ticks
  2077.     mov ax,13532
  2078.     mov dx,18               ;dx:ax := 1193180
  2079.     mov cx,bit_rate         ;cx := bit_rate
  2080.     mov bx,cx
  2081.     shr bx,1                ;bx := bit_rate/2
  2082.     add ax,bx               ;dx:ax := 1193180 + bit_rate/2
  2083.     adc dx,0
  2084.     div cx                  ;ax := (1193180 + bit_rate/2) div bit_rate
  2085.     mov cl_bit_len,ax       ;save bit length
  2086.     shr ax,1                ;ax := bit_length /2
  2087.     adc ax,0                ;if carry then ax+=1
  2088.     mov cl_bit_len_2,ax     ;save half bit length
  2089.     
  2090.     mov ax,100
  2091.     sub ax,dcd_thres        ;ax := 100 - dcd_thres
  2092.     mul cl_bit_len          ;ax := ((100 - dcd_thres)*cl_bit_len)
  2093.     mov cx,400
  2094.     div cx                  ;ax := ax div 400
  2095.     mov cl_dcd_thres,ax     ;save threshold for software DCD
  2096.     
  2097.     mov ax,cl_bit_len       ;compute walk_step for DPLL
  2098.     xor dx,dx
  2099.     mov cl,walk_step_div
  2100.     xor ch,ch
  2101.     div cx
  2102.     mov walk_step,ax
  2103.  
  2104.     mov ah,bd_bit_len
  2105.     mov al,slot_time
  2106.     mul ah                  ;ax := bd_bit_len * slot_time
  2107.     mov bd_slot_time,ax
  2108.  
  2109.     pop dx
  2110.     pop cx
  2111.     pop bx
  2112.     pop ax
  2113.     ret
  2114.  
  2115. PrintParameters:                ;prints actual parameters of the driver
  2116.     push ax
  2117.     push bx
  2118.     push cx
  2119.     push dx
  2120.     call print_following_string
  2121.     db 'Actual ax25 driver parameters:',13,10,0
  2122.  
  2123.     call Print_following_string
  2124.     db 'Service interrupt ',BOLD,'0x',0
  2125.     mov dl,packet_int_no
  2126.     call Print_DL_hex
  2127.     call Print_following_string
  2128.     db NORM,13,10,'COM I/O base ',BOLD,'0x',0
  2129.     mov dx,com_base
  2130.     call Print_DX_hex
  2131.  
  2132.     call Print_following_string
  2133.     db NORM,'  IRQ ',BOLD,'0x',0
  2134.     mov dl,com_irq
  2135.     call Print_DL_hex
  2136.  
  2137.     mov al,com_irq_prio
  2138.     and al,al
  2139.     jz skip_irq_prio
  2140.       call Print_following_string
  2141.       db ' [prior]',0
  2142. skip_irq_prio:
  2143.  
  2144.     mov al,com_irq_share
  2145.     and al,al
  2146.     jz skip_irq_shared
  2147.       call Print_following_string
  2148.       db ' [shared]',0
  2149. skip_irq_shared:
  2150.  
  2151.     call Print_following_string
  2152.     db NORM,'  Bit rate ',BOLD,0
  2153.     mov dx,bit_rate
  2154.     mov cx,dx                       ;keep data rate in cx
  2155.     call Print_DX_dec
  2156.     call Print_following_string
  2157.     db ' bps',NORM,13,10,'Tx head ',BOLD,0
  2158.     mov dx,tx_head
  2159.     call Print_DX_dec
  2160.     call Print_following_string
  2161.     db ' bits ',NORM,'(',0
  2162.     mov ax,1000     ;compute tx_head in ms units
  2163.     mul dx
  2164.     div cx          ;ax=tx_head*1000/bit_rate
  2165.     mov dx,ax
  2166.     call print_DX_dec
  2167.     call Print_following_string
  2168.     db 'ms)   Tx tail ',BOLD,0
  2169.     mov dx,tx_tail
  2170.     call Print_DX_dec
  2171.     call Print_following_string
  2172.     db ' bits ',NORM,'(',0
  2173.     mov ax,1000     ;compute tx_tail in ms units
  2174.     mul dx
  2175.     div cx          ;ax=tx_tail*1000/bit_rate
  2176.     mov dx,ax
  2177.     call Print_DX_dec
  2178.     call Print_following_string
  2179.     db 'ms)',13,10,'Slot time ',BOLD,0
  2180.     mov dl,slot_time
  2181.     xor dh,dh
  2182.     call Print_DX_dec
  2183.     call Print_following_string
  2184.     db ' bits ',NORM,'(',0
  2185.     mov ax,1000     ;compute slot_time in ms units
  2186.     mul dx
  2187.     div cx          ;ax=slot_time*1000/bit_rate
  2188.     mov dx,ax
  2189.     call Print_DX_dec
  2190.     call Print_following_string
  2191.     db 'ms)   p-persistance ',BOLD,0
  2192.     mov dl,persistance
  2193.     xor dh,dh
  2194.     call Print_DX_dec
  2195.     call Print_following_string
  2196.     db NORM,'/255',13,10,'Carrier mode: ',BOLD,0
  2197.     mov bl,carrier_sense
  2198.     xor bh,bh
  2199.     add bx,bx
  2200.     mov bx,[msg_carrier_modes+bx]
  2201.     call Print_BX_string
  2202.     call Print_following_string
  2203.     db 13,10,NORM,0
  2204.     
  2205.     mov al,carrier_sense
  2206.     cmp al,3
  2207.     jnz PrintSoundParam
  2208.       call Print_following_string
  2209.       db 'Software DCD threshold: ',BOLD,0
  2210.       mov dx,dcd_thres
  2211.       call Print_DX_dec
  2212.       call Print_following_string
  2213.       db NORM,13,10,0
  2214. PrintSoundParam:
  2215.     mov al,sound
  2216.     and al,al
  2217.     jz PrintParameters_ret
  2218.       call Print_following_string
  2219.       db BOLD,'Sound effects',NORM,' activated',13,10,0
  2220. PrintParameters_ret:
  2221.     call Print_following_string
  2222.     db 13,10,0
  2223.     
  2224.     pop dx
  2225.     pop cx
  2226.     pop bx
  2227.     pop ax
  2228.     ret
  2229.  
  2230.     even
  2231. msg_carrier_modes   label word
  2232.     dw offset Msg_full_duplex
  2233.     dw offset Msg_sense_DCD
  2234.     dw offset Msg_sense_trans
  2235.     dw offset Msg_sense_DPLL
  2236.  
  2237. Msg_full_duplex db 'do not sense carrier => full duplex',0
  2238. Msg_sense_DCD   db 'sense DCD modem line',0
  2239. Msg_sense_trans db 'sense data transitions',0
  2240. Msg_sense_DPLL  db 'deliver carrier from data analysis',0
  2241.  
  2242. ReadDecDigit:           ;ds:bx points to a character
  2243.     mov al,[bx]
  2244.     cmp al,'0'
  2245.     jc ReadDecDigit_ret     ;jump if below '0'
  2246.       cmp al,'9'+1
  2247.       cmc
  2248.       jc ReadDecDigit_Ret   ;jump if above '9'
  2249.         sub al,'0'
  2250. ReadDecDigit_ret:
  2251.     ret             ;carry=1 if not a dec digit => then al=the character
  2252.             ;otherwise al=digit value
  2253.  
  2254. ReadDecNumber:          ;ds:bx points to the first digit
  2255.     push cx
  2256.     mov dx,0        ;dx will contain the number
  2257. ReadNextDecDigit:
  2258.     call ReadDecDigit       ;read next digit - al=digit value
  2259.     jc ReadDecNumber_ret    ;jump if this was not a digit
  2260.       inc bx                ;increment pointer
  2261.       xor ah,ah             ;ax=digit value
  2262.       add dx,dx             ;multiply dx by 2
  2263.       mov cx,dx             ;multiply dx by 5
  2264.       add dx,dx
  2265.       add dx,dx
  2266.       add dx,cx
  2267.       add dx,ax             ;add the digit value just read
  2268.     jmp short ReadNextDecDigit
  2269. ReadDecNumber_ret:
  2270.     pop cx
  2271.     ret             ;dx=the number,
  2272.             ;ds:bx *char where the interpretation stopped
  2273.             ;al=this character, ah possibly modified
  2274.  
  2275. ;the routine below accepts only _lowercase_ letters as hex numbers
  2276. ReadHexDigit:           ;ds:bx = digit pointer
  2277.     mov al,[bx]
  2278.     cmp al,'0'
  2279.     jc ReadHexDigit_ret     ;jump if below '0'
  2280.       cmp al,'9'+1
  2281.       cmc
  2282.       jc ReadHexDigit_lett   ;jump if above '9'
  2283.         sub al,'0'
  2284.         jmp short ReadHexDigit_ret
  2285. ReadHexDigit_lett:
  2286.     cmp al,'a'
  2287.     jc ReadHexDigit_ret       ;jump if below 'a'
  2288.       cmp al,'f'+1
  2289.       cmc
  2290.       jc ReadHexDigit_ret     ;jump if above 'f'
  2291.         sub al,'a'-10
  2292. ReadHexDigit_ret:
  2293.     ret             ;carry=1 => not a hex digit, al=char.
  2294.             ;carry=0 => hex digit indeed, al=value
  2295.  
  2296. ReadHexNumber:
  2297.     push cx
  2298.     mov cl,4
  2299.     mov dx,0
  2300. ReadNextHexDigit:
  2301.     call ReadHexDigit
  2302.     jc ReadHexNumber_ret
  2303.       inc bx
  2304.       shl dx,cl     ;multiply dx by 16
  2305.       or dl,al      ;add the digit just read
  2306.     jmp short ReadNextHexDigit
  2307. ReadHexNumber_ret:
  2308.     pop cx
  2309.     ret             ;dx=the number,
  2310.             ;ds:bx *char where the interpretation stopped
  2311.             ;al=this character
  2312.  
  2313. ;==============================================================
  2314.  
  2315. ax25_code       ends
  2316.  
  2317.     end start
  2318.